package cn.taskservice.redis.idempotent.aspect;

import cn.taskservice.redis.idempotent.annotation.Idempotent;
import cn.taskservice.redis.idempotent.config.IdempotentExecutor;
import cn.taskservice.redis.idempotent.service.IdempotentFallback;
import cn.taskservice.redis.lock.util.SpELParserUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Slf4j
@Aspect
@Component
public class IdempotentAspect {
    @Autowired
    private IdempotentExecutor executor;

    @Autowired
    private SpELParserUtil spelParserUtil;

    @Autowired
    private ApplicationContext applicationContext;

    @Around("@annotation(idempotent)")
    public Object around(ProceedingJoinPoint point, Idempotent idempotent) throws Throwable {
        long start = System.currentTimeMillis();

        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = AopUtils.getMostSpecificMethod(signature.getMethod(), point.getTarget().getClass());

        String rawKey = spelParserUtil.parse(idempotent.key(), method, point.getArgs());
        String finalKey = buildRedisKey(idempotent.prefix(), rawKey, idempotent.useHashTag());
        long expireMillis = idempotent.expireMillis();

        boolean acquired = executor.tryAcquire(finalKey, expireMillis);

        if (acquired) {
            log.info("[幂等校验] key={} => 获取成功，执行业务逻辑", finalKey);
            Object result = point.proceed();
            log.info("[幂等校验] key={} => 执行完成，耗时 {}ms", finalKey, System.currentTimeMillis() - start);
            return result;
        } else {
            log.warn("[幂等校验] key={} => 拦截重复请求", finalKey);
            Class<? extends IdempotentFallback> fallbackClass = idempotent.fallback();
            if (!fallbackClass.equals(IdempotentFallback.class)) {
                IdempotentFallback fallback = applicationContext.getBean(fallbackClass);
                return fallback.handle(finalKey, point);
            }
            throw new RuntimeException("重复请求，已拦截");
        }
    }

    private String buildRedisKey(String prefix, String rawKey, boolean useHashTag) {
        if (useHashTag) {
            return prefix + ":{" + rawKey + "}";
        } else {
            return prefix + ":" + rawKey;
        }
    }
}
